/**
 * Copyright (c) 2009 blurredVision
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#ifndef __PARTICLE_SYSTEM_H__
#define __PARTICLE_SYSTEM_H__

#pragma once

#include "sg/SceneNode.h"
#include "Particle.h"
#include "ITexture.h"
#include "IShaderEffect.h"

#include <vector>

namespace particlesystem {

/**
 * \ingroup CoreEngine
 *
 * The IEmitter class.<br>
 * Base of the particle system. It is positioned inside the scene.<br>
 * The IEmitter handles particle creation and lifecycle and stores initial parameters of the particles.
 *
 * \date 04-10-2007
 *
 * \author juckel
 * 
 * \todo Write setter for parameters and add more parameters
 *
 */
class DLL_EXPORT IEmitter :
	public sg::SceneNode
{
public:
	/// Create a new particle system
	static IEmitter* Create(const math::Vector3 &position = math::Vector3(0,0,0));

	/// Initialize the default values for particle system generation
	bool init(unsigned int maxParticles, double maxLife, double genDelta, const math::Vector3 &initVel, float variance = .3f);

	/// Load a shader particle system and a texture to use
	bool load(const std::string &shader, const std::string &texture);

	/// Update the generation cycle
	void update(double dt);

	/// Draw the particle system
	void draw();

	/// Starts the generation of new particles
	void start();

	/// Stops the generation of new particles after x seconds
	void stop(double timer = 0);

	// Pause / Unpause the emitter
	void pause(bool value);

	/// Set the maximum lifetime of an individual particle
	void setMaxLifetime(double maxlife);

	/// Set the timestep between individual particle generation
	void setGenerationDelta(double gendelta);

	/// Set the initial velocity of newly created particles. 
	/// A random variance between 0 and the variance factor is added to every component of the initial velocity.
	void setInitialVelocity(const math::Vector3 &initVel, float variance = .3f);

	/// Set the initial color value for newly generated particles
	void setInitialColor(const math::Vector4 &color);

	/// Indicates if an Emitter is finalized, i.e. stopped and all particles have died
	const bool isFinalized() const;

	/// Calculates the bounding sphere for visibility testing
	virtual void calculateBoundingSphere();

	/// Set the offset from the position of the emitter for generating new particles
	void setOffset(const math::Vector3 offset);

	/// Return the offset
	const math::Vector3& getOffset() const;

	/// Return the variance value
	const float getVariance() const;

	/// Overwrite the setTransform and updateVisibility from sg::SceneNode
	void setTransform(ITransformable *trans);
	void updateVisiblity(Frustum *frustum);

	void setNormal(const math::Vector3 &normal);

	static int GetNumEmitter();
	static void LimitEmitter(bool val);

	/// Paricle class has access to protected members
	friend class Particle;
protected:
	IEmitter(void);
	virtual ~IEmitter(void);

	virtual void updateData() = 0;
	virtual void drawParticles() = 0;

	ITransformable *m_pSpawnTrans;

	static math::Vector3 vGravity;

	IShaderEffect *m_pEffect;			///< The effect used to draw the particle system
	ITexture      *m_pTexture;			///< The texture used for particles

	double         m_dDT;				///< The time difference since the last particle was created

	math::Vector3  m_vInitialVelocity;	///< The direction the IEmitter is pointing to
	float          m_fVariance;			///< Variance added to the initial velocity
	float          m_fHalfVar;			///< The half of the variance added to the initial velocity

	double         m_dGenerationDelta;	///< The number of seconds between generation of individual particles
	int            m_iMaxParticles;		///< The maximum number of particle, the system can store

	bool           m_bStarted;			///< Indicates if the emitter was started
	bool           m_bPaused;			///< Indicates if the emitter is paused. Paused Emitter don't generate particles, but are not deleted
	bool		   m_bFinalized;		///< Indicates if the particle system is finalized. Will be set if the system was started once and no particle are alive any more

	double         m_dStartTime;		///< The time the system was started
	double         m_dTimer;
	double         m_dGenDuration;		///< The lifetime of the whole system

	double         m_dPartMaxLifetime;
	float          m_fPartMinSize;
	float          m_fPartMaxSize;
	float          m_fPartSizeVar;
	float          m_fPartWeight;
	math::Vector4  m_vPartInitColor;
	math::Vector4  m_vPartFadeColor;

	math::Vector3  m_vOffset;

	math::Vector3  m_vNormal;

	int            viewportheight;
	bool           m_bNeedsSorting;
	Particle*      m_pParticles;		///< A list of the particles inside the system
	int            m_iNumAlive;
	bool           m_bDataUpdate;		///< Indicates if the data needs to be copied into the vertex buffer

private:
	SHADERHANDLE hTexHandle;
	SHADERHANDLE hViewPortHeight;
	
	static int s_iNumEmitter;			///< The total amount of emitters currently in the scene
	static bool s_bLimitEmitter;
	static std::map<math::Vector3, IEmitter*> s_lEmitter;
};

} // namespace

#endif
